瓦片预缓存 Sample详情

最后更新时间:2019年6月17日

功能介绍

在线地图服务是移动终端常用的出图方式,在线地图服务通常是以瓦片为出图单元,最终将瓦片按照一定的规则拼接出图。为了给用户提供流畅、高效、快速的地图浏览体验,MapGIS Mobile不仅采用了高性能的地图渲染引擎,还提供了自动缓存的策略。在加载在线瓦片地图时,系统会自动将浏览过的范围、级别的地图进行自动缓存,存储到移动设备中,再次显示该区域的地图时直接读取离线本地数据,不用请求在线服务,从而使得地图能够非常迅速地渲染展示。这是MapGIS Mobile提供的自动缓存策略,存在一定的局限性,对于没有浏览过的区域的地图不会进行缓存。这无法满足要求在断网情况下同样能够浏览任意级别、任意区域地图的场景。所以MapGIS Mobile提供了瓦片预缓存的功能。

为了提高在线瓦片地图的加载速度,地图瓦片可以在终端预生成本地缓存,客户端浏览地图时可直接读取,这种技术被称作为瓦片预缓存技术。对于移动端来说,受限于硬件水平、网络速度、存储空间等因素,实现与PC终端相同的地图浏览及交互体验却相当困难,瓦片预缓存技术便在一定程度上解决了上述问题;另一方面,瓦片预缓存在在线浏览地图时动态缓存到本地存储,当在离线的时候亦可以浏览相同地图,这在野外作业中尤为重要。

瓦片预缓存实现的原理:在网络通畅情况下,根据用户需求,按照范围、级别将在线地图瓦片在终端生成本地缓存瓦片,存储到本地数据库中。从而在网络未连接的情况下,客户端直接读取本地缓存瓦片,同样能够进行地图的展示浏览。

MapGIS Mobile支持多种来源的在线地图,其中能够使用瓦片预缓存功能的数据类型包括:MapGIS IGServer在线瓦片地图、WMS地图服务、WMTS地图服务、天地图、Google地图、百度地图、高德地图、OSM地图、Bing地图等。

在缓存时,可以设置地图的级别范围、地图范围来实现更精确、更有针对性的瓦片缓存。

功能接口

瓦片预缓存功能对应API程序包为com.zondy.mapgis.core.map,其核心类为服务图层类ServerLayer,核心接口如下所示:

接口 说明
setCacheLocation() 设置缓存路径
setTilePreFetchListener() 设置预缓存状态监听
preFetch() 预缓存
stopFetch() 停止预缓存
clearCache() 清除缓存

实现方法

瓦片预缓存的大体实现方法如下所示:

(1)加载地图:首先需要加载能够进行瓦片预缓存功能的在线地图,并获取待进行缓存的ServerLayer图层对象;

(2)准备预缓存条件:选择需要缓存的地图范围、瓦片级别范围;

(3)设置监听:利用setTilePreFetchListener接口监听瓦片缓存的进度、状态;

(4)开始缓存:调用preFetch方法开始缓存;

(5)显示缓存:在缓存完成之后,可调用缓存进行显示。

实现过程

1

加载地图

加载在线瓦片地图,以IGServer瓦片地图服务为例。

//地图服务对象
MapServer mapServer = ServerLayer.createMapServer(MapServer.MAPSERVER_TYPE_IGSERVER_TILE);
//设置地图服务地址
mapServer.setURL("http://develop.smaryun.com:6163/igs/rest/mrms/tile/WorldJWTile");
//为mapserver设置名称,创建的缓存文件会使用到此名称
mapServer.setName("IGServer瓦片服务");
//服务图层:igserver中发布的瓦片服务
ServerLayer serverLayer = new ServerLayer();
//设置缓存路径
serverLayer.setCacheLocation(FilePath.PHONE_SDCARD_PATH + "/MapGIS Mobile 2D Sample/TileCache/");
//为服务图层设置地图服务
serverLayer.setMapServer(mapServer);
//地图对象
map = new Map();
map.append(serverLayer);
//加载地图
mapView.setMapAsync(map, new MapViewFinishCallback() {
    @Override
    public void onDidFinish(boolean arg0) {
        if (arg0) {
            //地图加载成功
        }
    }
});

代码说明:(1)进行瓦片预缓存的ServerLayer服务图层,需定义为成员变量。(2)设置缓存路径必须要在调用setMapServer之前执行,因为调用setMapServer的时候就会自动创建缓存了。

2

准备预缓存条件

在进行瓦片缓存时,需要设置缓存的地图范围,由于瓦片地图采用了分级显示的机制,所以进行缓存时,也需要选择缓存的瓦片级别。在缓存时将会只缓存此地图范围内设置的级别的地图。设置的地图范围越小,缓存消耗时间越少,缓存后的文件数据量越小。选择的瓦片级别范围越多,消耗时间越多。级别越大的瓦片,缓存时消耗的时间越多。根据实际需求选择合理的地图范围、瓦片级别范围。

private void getServerInfo(ServerLayer serverlayer) {
    //获取地图服务对象
    final MapServer mapServer = serverlayer.getMapServer();
    //连接数据,在缓存igserver中的瓦片服务的时候需要使用
    new Thread(new Runnable() {
        @Override
        public void run() {
            //需要先成功连接数据,才能获取其信息
            long a = mapServer.connectData();
            if (a > 0) {
                Message m = new Message();
                m.what = 1;
                mhandler.sendMessage(m);
            } else {
                //连接数据失败,请确保网络已连接
            }
        }
    }).start();

    mhandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //转换为瓦片服务,获取最小最大级别
            if (mapServer instanceof TileMapServer) {
                TileMapServer tileServer = (TileMapServer) mapServer;
                minZoom = tileServer.getMinZoom();
                maxZoom = tileServer.getMaxZoom();
            }
        }
    };
}

3

设置缓存状态监听

在缓存之前,调用setTilePreFetchListener接口为服务图层设置缓存监听器,在其回调函数中可获取缓存的状态、进度、结果信息。需注意的是这些回调函数都是在子线程中执行的,所以如果需要讲缓存进度信息展示在视图中,需在主线程中处理。

//设置预缓存状态监听
serverLayer.setTilePreFetchListener(tilePreFetchListener);
//瓦片缓存监听器
TilePreFetchListener tilePreFetchListener = new TilePreFetchListener() {
    /**
     * 预缓存进度监听
     *
     * @param lTaskID                    任务ID,参见:ServerLayer.preFetch()
     * @param lTotalTileCount            待预缓存的瓦片总数
     * @param lCurTileIndex              当前缓存的瓦片索引,从0开始
     * @param dCurTileFetchProgress      当前缓存进度[0,100]
     */
    public void onProgressing(int lTaskID, int lTotalTileCount, int lCurTileIndex, final double dCurTileFetchProgress) {
        Log.e("PreFetch", "预缓存进度:任务ID:" + lTaskID + ",待预缓存的瓦片总数:" + lTotalTileCount + ",当前缓存的瓦片索引:" + lCurTileIndex + ",当前缓存进度:" + dCurTileFetchProgress);
    }

    /**
     * 缓存状态监听
     *
     * @param lTaskID         任务ID,参见:ServerLayer.preFetch()
     * @param lZoom           当前缓存瓦片的级别
     * @param lRow            当前缓存瓦片的行号
     * @param lCol            当前缓存瓦片的列号
     * @param iStatus         当前缓存瓦片的状态:0:成功,1:获取瓦片失败,2:写入失败.
     */
    public void onTileFetched(int lTaskID, int lZoom, int lRow, int lCol, short iStatus) {
        Log.e("PreFetch", "缓存状态:任务ID:" + lTaskID + ",当前缓存瓦片级别:" + lZoom + ",当前缓存瓦片行号:" + lRow + ",当前缓存瓦片列号:" + lCol + ",当前缓存瓦片状态:" + iStatus);
    }

    /**
     * 预缓存结束监听
     *
     * @param lTaskID                任务ID,参见:ServerLayer.preFetch()
     * @param iFinishStatus          0:正常结束,1:非正常结束
     */
    public void onFetchFinish(int lTaskID, short iFinishStatus) {
        Log.e("PreFetch", "预缓存结束监听:任务ID:" + lTaskID + ",缓存结果:" + iFinishStatus);
        if (lTaskID == taskID) {
            if (iFinishStatus == 0){
                //缓存成功
            }
        }
    }
};

4

开始缓存

准备工作完成后,可调用preFetch方法,指定缓存的最小最大级别、地图空间范围开始缓存,返回值为缓存的任务ID。缓存的级别可从第0级到第0级,下载的即为第0级的瓦片。在缓存时,需要确保网络状态良好,避免造成缓存失败的情况。

/**
 * 预缓存(异步方法)
 * 参数:预缓存的最小级别,预缓存的最大级别,预缓存的空间范围
 */
int taskID = serverLayer.preFetch(min, max, rect);

5

显示缓存

根据缓存监听可判断缓存结果,缓存成功完成后,会在缓存路径中生成一个“**.mtdb”缓存文件。缓存完成后,可调用离线缓存,在未连接网络时同样能够显示地图。

在调用缓存显示地图时,与普通的通过在线请求显示地图方法类似,另外需调用setAccessMode方法设置从缓存中读取数据,并调用setCacheLocation方法设置缓存数据存在的路径,关键代码如下所示:

//地图服务对象
MapServer mapServer = ServerLayer.createMapServer(MapServer.MAPSERVER_TYPE_IGSERVER_TILE);
//设置地图服务地址
mapServer.setURL("http://develop.smaryun.com:6163/igs/rest/mrms/tile/WorldJWTile");
//为mapserver设置名称,创建的缓存文件会使用到此名称
mapServer.setName("IGServer瓦片服务");
//服务图层:igserver中发布的瓦片服务
ServerLayer serverLayer = new ServerLayer();
//设置服务图层的数据只从缓存中读取
serverLayer.setAccessMode(MapServerAccessMode.CacheOnly);
//设置缓存数据的路径
serverLayer.setCacheLocation(FilePath.PHONE_SDCARD_PATH + "/MapGIS Mobile 2D Sample/TileCache/");
//为服务图层设置地图服务
serverLayer.setMapServer(mapServer);
//地图对象
map = new Map();
map.append(serverLayer);
//加载地图
mapView.setMapAsync(map, new MapViewFinishCallback() {
    @Override
    public void onDidFinish(boolean arg0) {
        if (arg0) {
            //地图加载成功
        }
    }
});